package c.c.d.s.s.o.c.as.configuration.csrf;

import c.c.d.s.s.o.c.as.configuration.csrf.strategy.AbstractCsrfStrategy;
import c.c.d.s.s.o.c.as.configuration.csrf.strategy.ImplicitCsrfStrategy;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.RedirectView;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Description: {@link org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint} 切面.<br>
 * Details: 在颁发 ACCESS-TOKEN 后, 生成 CSRF-TOKEN, 将其缓存并置入响应头.<br>
 * 执行顺序:
 * <ol>
 *     <li>org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint#authorize(..)</li>
 *     <li>org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint#approveOrDeny(..)</li>
 * </ol>
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-08-12 11:22
 */
@Slf4j
@Aspect
@Component
public class AuthorizationEndpointCsrfStrategyAspect extends AbstractCsrfStrategyAspect {

    /**
     * 所有支持 CSRF 策略的授权模式
     */
    private final List<? extends AbstractCsrfStrategy> delegates = Collections.singletonList(new ImplicitCsrfStrategy());

    @Pointcut(
            "execution(* org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.approveOrDeny(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping))"
    )
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        final Object any = proceedingJoinPoint.proceed();

        try {
            final RedirectView redirectView = (RedirectView) any;

            // ~ 获取请求参数
            final Map<String, ?> parameters = super.extractParameters(proceedingJoinPoint);

            // ~ 获取匹配的策略
            final AbstractCsrfStrategy strategy = super.decide(parameters, delegates);

            if (Objects.isNull(strategy)) {
                log.warn("No eligible strategy.");
                return redirectView;
            }

            log.debug("Eligible strategy {} for grant type: {}.", strategy.getClass().getSimpleName(), strategy.getGrantType().getCode());

            // ~ 执行策略并返回 CSRF-TOKEN
            final String csrfToken = strategy.execute(parameters);

            // ~ 置入 CSRF-TOKEN
            redirectView.setUrl(redirectView.getUrl() + "&csrf_token=" + csrfToken);
            return redirectView;
        } catch (ClassCastException exception) {
            exception.printStackTrace();
            log.warn(exception.getMessage());
            return any;
        }
    }

}
